package com.xianyun.smartpicturebackend.aop;

import com.xianyun.smartpicturebackend.annotation.AuthCheck;
import com.xianyun.smartpicturebackend.exception.BusinessException;
import com.xianyun.smartpicturebackend.exception.ErrorCode;
import com.xianyun.smartpicturebackend.model.entity.User;
import com.xianyun.smartpicturebackend.model.enums.UserRoleEnum;
import com.xianyun.smartpicturebackend.service.UserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @author xianYun
 * @version 1.0
 **/
@Aspect
@Component
public class AuthInterception {
    @Resource
    private UserService userService;

    /**
     * 切面拦截方法，判断用户权限
     *
     * @return
     */
    //  @annotation(authCheck) ： 表示在只拦截方法上有AuthCheck注解的方法
    //  @annotation()是一个切入点匹配器
    @Around("@annotation(authCheck)")
    public Object doInterception(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        String mustRole = authCheck.mustRole();//被 @AuthCheck 注解的方法，必须具有mustRole角色
        //通过通用的全局上下文，获取当前请求的所有属性
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        /*
        RequestAttributes 直接拿不到当前的请求，所以需要强转为 ServletRequestAttributes
         */
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        //获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        //将注解上的mustRole转换成枚举类型，方便判断
        UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
        //如果该方法不需要权限，则直接执行
        if (mustRoleEnum == null) {
            return joinPoint.proceed();
        }
        //以下的代码必须有权限才会执行的方法
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());
        if (userRoleEnum == null) {//如果用户没有角色，则抛出异常（角色的不同，系统权限也不同）
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        //必须要有管理员权限才能执行的方法，但用户没有管理员权限
        if (UserRoleEnum.ADMIN.equals(mustRoleEnum) && !UserRoleEnum.ADMIN.equals(userRoleEnum)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        //执行的方法需要管理员权限，用户也有管理员权限，则执行方法
        return joinPoint.proceed();
    }
    /*
    注解+AOP实现用户权限控制的思路：
    自定义一个权限注解，注解的值为用户角色（不同用户角色对应不同权限）
    在需要权限才能执行的方法上加上该注解，并指定对应的角色
    在切面拦截方法中实现用户权限校验：
        1. 获取当前登录用户（可以通过 RequestContextHolder.current() 获取当前所有的请求属性）
        2. 获取当前方法上注解的权限角色
        3. 判断当前登录用户是否具有该权限
        4. 如果没有权限，则抛出异常
        5. 如果有权限，则执行方法
     */

}
